﻿using System.Reflection;
using System.Security;
using System.IO;
using System.Net.Sockets;
using Honeycomb.Core;
using Honeycomb.Core.Client;

namespace Honeycomb.Client.Console
{
    class Program
    {
        /// <summary>
        /// Number of tasks to buffer.
        /// </summary>
        const int taskBufferLength = 1;

        /// <summary>
        /// Entrypoint.
        /// </summary>
        /// <param name="args">Command line parameters.</param>
        static void Main(string[] args)
        {
            System.Console.WriteLine("Honeycomb Grid Console Client " + Assembly.GetExecutingAssembly().GetName().Version);
            System.Console.WriteLine("");
            if (args.Length != 1)
            {
                System.Console.WriteLine("Usage: CClient.exe [uri]");
                System.Console.WriteLine("");
                System.Console.WriteLine("Example: CClient.exe grid://host.com");
                return;
            }

            GridUri uri = new GridUri(args[0]);

            try
            {
                System.Console.WriteLine("Attempting to connect to: " + uri + " ...");

                using (AssemblyManager assemblyManager = new AssemblyManager())
                using (TaskScheduler taskScheduler = new TaskScheduler(taskBufferLength))
                using (GridClient client = new GridClient(uri))
                {

                    System.Console.WriteLine("Client connected. Press CTRL+C to quit.");
                    System.Console.Write("Processing tasks ");

                    // Reconnect loop.
                    for (; ; )
                    {
                        try
                        {
                            // Buffer tasks loop.
                            for (int i = 0; i < taskBufferLength; i++)
                            {
                                // Request task.
                                byte[] serializedTask = client.RequestTask();

                                // Assembly request loop.
                                for (;;)
                                {
                                    string assemblyName = assemblyManager.GetRequiredAssembyName(serializedTask);
                                    if (assemblyName == null)
                                        break;

                                    // Request and load assembly.
                                    assemblyManager.LoadAssembly(client.RequestAssembly(assemblyName));
                                }

                                // Schedule task, exit loop when buffer is full.
                                // TODO: If we only have fewer than the task buffer size to process, we should not sit around
                                // waiting for more coming in. Timer?
                                if (taskScheduler.Schedule(TaskBase.Deserialize(serializedTask)))
                                    break;
                            }

                            // Send task result loop.
                            for (;;)
                            {
                                TaskExecutionResult executionResult = taskScheduler.GetExecutionResult();
                                if (executionResult == null)
                                    break;

                                // Return task result.
                                client.SendResult(executionResult);
                                System.Console.Write(".");
                            }
                        }
                        catch (SocketException)
                        {
                            // TODO: Client should attempt reconnect at this stage since it could be temporary offline. 
                            // TODO: If the hostname is not resolved it should give an error message
                            System.Console.WriteLine("Cannot connect to server!");
                        }
                        catch (IOException)
                        {
                            // TODO: Client should attempt reconnect at this stage.
                            System.Console.WriteLine("Connection to server lost!");
                        }
                    }
                }
            }
            catch (SecurityException ex)
            {
                System.Console.WriteLine(ex.Message);
                return;
            }
            catch (System.Exception ex)
            {
                System.Console.WriteLine(ex.Message);
            }
        }
    }
}